package com.platform.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.google.common.base.Preconditions;
import com.platform.comm.service.impl.CommonBizImpl;
import com.platform.comm.util.ProjectUtils;
import com.platform.comm.util.UploadUtil;
import com.platform.core.repository.SysMsgManageRepository;
import com.querydsl.core.BooleanBuilder;
import com.platform.comm.dto.DtoMapper;
import com.platform.comm.entity.CommDataInfoEntity;
import com.platform.comm.enums.YnEnum;
import com.platform.comm.exceptions.RestApiException;
import com.platform.comm.jwt.JwtTokenUtil;
import com.platform.comm.jwt.TokenInfoUtil;
import com.platform.comm.util.DateTimeUtil;
import com.platform.core.entity.QSysMsgManageEntity;
import com.platform.core.entity.QSysMsgRecordEntity;
import com.platform.core.entity.QSysUser;
import com.platform.core.entity.SysMsgManageEntity;
import com.platform.core.service.SysMsgManageBiz;
import com.platform.core.service.SysMsgRecordBiz;
import com.platform.core.service.SysUserService;
import com.platform.core.utils.EmailUtil;
import com.platform.dto.SysMsgManageVo;
import com.platform.dto.SysMsgRecordVo;
import com.platform.dto.UserQo;
import com.platform.enums.MsgNotifyType;
import com.platform.oss.core.StandardOssClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Description: LcodeMsgManage-业务接口实现类
 *
 * @author Coder
 */
@Slf4j
@Service
public class SysMsgManageBizImpl extends CommonBizImpl<SysMsgManageRepository, SysMsgManageEntity, String> implements SysMsgManageBiz {

    private final QSysMsgManageEntity qLcodeMsgManage = QSysMsgManageEntity.sysMsgManageEntity;
    private final QSysMsgRecordEntity qSysMsgRecordEntity = QSysMsgRecordEntity.sysMsgRecordEntity;
    private final QSysUser qSysUser = QSysUser.sysUser;
    private final DtoMapper mapper;
    private final TokenInfoUtil infoUtil;
    private final SysMsgRecordBiz msgRecordBiz;
    private final SysUserService userService;
    private final CommDataInfoBizImpl commDataInfoBiz;

    @javax.annotation.Resource(name="${oss.defaultBeanName}")
    private StandardOssClient standardOssClient;

    public SysMsgManageBizImpl(SysMsgManageRepository lcodeMsgManageRepository, DtoMapper mapper,
                               TokenInfoUtil infoUtil, SysMsgRecordBiz msgRecordBiz, SysUserService userService,
                               CommDataInfoBizImpl commDataInfoBiz) {
        super(lcodeMsgManageRepository);
        this.mapper = mapper;
        this.infoUtil = infoUtil;
        this.msgRecordBiz = msgRecordBiz;
        this.userService = userService;
        this.commDataInfoBiz = commDataInfoBiz;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addMsg(SysMsgManageVo manageVo) {
        JwtTokenUtil.JwtUser user = infoUtil.getUser();
        SysMsgManageEntity manageEntity = mapper.map(manageVo, SysMsgManageEntity.class);
        //新增消息
        manageEntity.setCreateBy(user.getId());
        manageEntity.setCreateDate(LocalDateTime.now());
        manageEntity.setFlag(YnEnum.Y.key());
        manageEntity.setExpireStatus(YnEnum.N.key());
        //设置过期时间 若选择过期天数，则按过期天数计算，否则默认3个月
        LocalDate expireTime = manageVo.getExpireTime();
        if (expireTime == null) {
            manageEntity.setExpireTime(LocalDate.now().plusDays(90));
        }
        SysMsgManageEntity msgManageEntity = saveEntity(manageEntity);
        //发送消息
        List<SysMsgRecordVo> msgRecordVos = new ArrayList<>();
        String notifyType = msgManageEntity.getNotifyType();

        //目前只处理邮件和站内信
        if (notifyType.equals(MsgNotifyType.EMAIL.name())) {
            //获取用户数据 用户、角色、部门
            List<UserQo> byUserId = userService.getByUserId(Arrays.asList(manageEntity.getReceiveValue().split(",")));

            String collect = byUserId.stream().filter(x -> StringUtils.isEmpty(x.getEmail())).collect(Collectors.toList())
                    .stream().map(UserQo::getRealName)
                    .collect(Collectors.joining(","));
            if (StringUtils.isNotEmpty(collect)) {
                throw new RestApiException(collect + "无邮箱相关内容，请到用户管理中补全信息");
            }

            List<UserQo> userQos = byUserId.stream().filter(x -> StringUtils.isNotEmpty(x.getEmail())).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(userQos)) {
                manageEntity.setReceiveValue(userQos.stream().map(UserQo::getId).collect(Collectors.joining(",")));
                getRecord(manageEntity, msgRecordVos);
                //添加消息记录
                msgRecordBiz.insertRecord(msgRecordVos);
                //发送邮件
                sendEmail(userQos.stream().map(UserQo::getEmail).collect(Collectors.joining(",")), manageEntity);
            }
        } else {
            getRecord(manageEntity, msgRecordVos);
            //添加消息记录
            msgRecordBiz.insertRecord(msgRecordVos);
        }
    }

    /**
     * 发送邮件
     */
    private void sendEmail(String emailAddress, SysMsgManageEntity manageEntity) {
        //文件保存本地
        String pageSource = ProjectUtils.getTemplateFilePath("uaa", "emailPicture");
        String path = UploadUtil.buildStdPath(pageSource, manageEntity.getId());
        List<String> fileIds = StringUtils.isBlank(manageEntity.getFileId()) ? new ArrayList<>() : Arrays.asList(manageEntity.getFileId().split(","));
        fileIds.forEach(x->{
            String basePath;
            CommDataInfoEntity dataInfoEntity = commDataInfoBiz.fetchById(x);
            InputStream download = standardOssClient.download(dataInfoEntity.getFileUrl());
            try {
                basePath = UploadUtil.buildStdPath(path, dataInfoEntity.getFileName());
                FileUtils.copyInputStreamToFile(download, new File(basePath));
            } catch (IOException e) {
                throw new RestApiException("上传文件失败：" + e.getMessage());
            }
        });
        File file = new File(path);
        File[] files = file.listFiles();
        EmailUtil.sendEmail(emailAddress, manageEntity.getMsgTitle(), manageEntity.getMsgContent(), files);
        //删除本地文件
        if (new File(path).exists()) {
            deleteDirectory(path);
        }
    }


    /**
     * 删除文件夹
     *
     * @param directory 文件夹名
     * @return 删除成功返回true, 失败返回false
     */
    public static boolean deleteDirectory(String directory) {
        // directory不以文件分隔符（/或\）结尾时，自动添加文件分隔符，不同系统下File.separator方法会自动添加相应的分隔符
        if (!directory.endsWith(File.separator)) {
            directory = directory + File.separator;
        }
        File directoryFile = new File(directory);
        // 判断directory对应的文件是否存在，或者是否是一个文件夹
        if (!directoryFile.exists() || !directoryFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        // 删除文件夹下的所有文件和文件夹
        File[] files = directoryFile.listFiles();
        // 循环删除所有的子文件及子文件夹
        for (File file : Objects.requireNonNull(files)) {
            // 删除子文件
            if (file.isFile()) {
                flag = deleteFile(file.getAbsolutePath());
            } else {
                // 删除子文件夹
                flag = deleteDirectory(file.getAbsolutePath());
            }
            if (!flag) {
                break;
            }
        }

        if (!flag) {
            return false;
        }
        // 最后删除当前文件夹
        return directoryFile.delete();
    }


    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return 删除成功返回true, 失败返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            return file.delete();
        }
        return false;
    }


    private void getRecord(SysMsgManageEntity manageEntity, List<SysMsgRecordVo> msgRecordVos) {
        String receiveValue = manageEntity.getReceiveValue();
        Arrays.asList(receiveValue.split(",")).forEach(x -> {
            SysMsgRecordVo msgRecordVo = new SysMsgRecordVo();
            msgRecordVo.setMsgId(manageEntity.getId());
            msgRecordVo.setReadStatus(YnEnum.N.key());
            msgRecordVo.setRecevieUser(x);
            msgRecordVo.setFlag(YnEnum.Y.key());
            msgRecordVos.add(msgRecordVo);
        });
    }


    @Override
    public void delete(Map<String, String> map) {
        String id = map.getOrDefault("id", "");
        Preconditions.checkArgument(StringUtils.isNotEmpty(id), "id不能为空");
        SysMsgManageEntity sysMsgManageEntity = fetchById(id);
        sysMsgManageEntity.setFlag(YnEnum.N.key());
        sysMsgManageEntity.setUpdateDate(LocalDateTime.now());
        sysMsgManageEntity.setUpdateBy(infoUtil.getUserId());
        saveEntity(sysMsgManageEntity);
    }

    @Override
    public SysMsgManageVo detail(Map<String, String> map) {
        String id = map.getOrDefault("id", "");
        Preconditions.checkArgument(StringUtils.isNotEmpty(id), "id不能为空");
        SysMsgManageEntity manageEntity = fetchById(id);
        SysMsgManageVo msgManageVo = mapper.map(manageEntity, SysMsgManageVo.class);
        List<SysMsgRecordVo> sysMsgRecordVos = msgRecordBiz.searchByMsgId(msgManageVo.getId());
        String message = sysMsgRecordVos
                .stream()
                .map(SysMsgRecordVo::getRecevieName)
                .collect(Collectors.joining(","));
        msgManageVo.setAddressee(message);
        JwtTokenUtil.JwtUser user = infoUtil.getUser();
        msgManageVo.setCreateName(user.getRealName());
        msgManageVo.setFileList(msgRecordBiz.getFileId(msgManageVo.getFileId()));
        return msgManageVo;
    }


    @Override
    public Page<SysMsgManageVo> search(Map<String, String> map, Pageable pageable) {
        String userId = infoUtil.getUserId();
        String msgTitle = map.get("msgTitle");
        String recevieName = map.get("recevieName");
        String notifyType = map.get("notifyType");
        String expireStatus = map.get("expireStatus");
        String beginDate = map.get("beginDate");
        String endDate = map.get("endDate");
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(qLcodeMsgManage.flag.eq(YnEnum.Y.key()));
        builder.and(qLcodeMsgManage.createBy.eq(userId));
        if (StringUtils.isNotEmpty(msgTitle)) {
            builder.and(qLcodeMsgManage.msgTitle.contains(msgTitle));
        }
        if (StringUtils.isNotEmpty(recevieName)) {
            builder.and(qSysUser.realName.contains(recevieName));
        }
        if (StringUtils.isNotEmpty(expireStatus)) {
            builder.and(qLcodeMsgManage.expireStatus.eq(expireStatus));
        }
        if (StringUtils.isNotEmpty(notifyType)) {
            builder.and(qLcodeMsgManage.notifyType.eq(notifyType));
        }
        if (StringUtils.isNotEmpty(beginDate)) {
            LocalDateTime newBegin = DateTimeUtil.parseStdLongTimeStr(beginDate + " 00:00:00");
            LocalDateTime newEnd = DateTimeUtil.parseStdLongTimeStr(endDate + " 23:59:59");
            builder.and(qLcodeMsgManage.createDate.between(newBegin, newEnd));
        }

        return this.repository.findAll(builder, pageable, jpqlQuery ->
                jpqlQuery.leftJoin(qSysMsgRecordEntity).on(qSysMsgRecordEntity.msgId.eq(qLcodeMsgManage.id))
                        .leftJoin(qSysUser).on(qSysUser.id.eq(qSysMsgRecordEntity.recevieUser)).distinct().orderBy(qLcodeMsgManage.createDate.desc()))
                .map(x -> mapper.map(x, SysMsgManageVo.class));
    }


    @Scheduled(cron = "0 1 0 * * ? ")
    public void editExpireStatus() {
        List<SysMsgManageEntity> sysMsgManageEntities = queryAll(qLcodeMsgManage.flag.eq(YnEnum.Y.key())
                .and(qLcodeMsgManage.expireStatus.eq(YnEnum.N.key())).and(qLcodeMsgManage.expireTime.lt(LocalDate.now())));
        if (CollUtil.isNotEmpty(sysMsgManageEntities)) {
            this.repository.saveAll(sysMsgManageEntities.stream().peek(x -> {
                x.setExpireStatus(YnEnum.Y.key());
                x.setUpdateDate(LocalDateTime.now());
                x.setUpdateBy(infoUtil.getUserId());
            }).collect(Collectors.toList()));
        }
    }
}
